﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Data.Objects;

using LinqKit;
using PagedList;

using LetLord.Models;
using LetLord.ViewModels;

namespace LetLord.Controllers
{
    public class SearchController : Controller
    {
        private readonly LetLordContext db;
        private readonly ITenantRepository tenantRepository;
        private readonly ILandlordRepository landlordRepository;

        public SearchController()
            : this(new TenantRepository(), new LandlordRepository())
        {   
        }

        public SearchController(ITenantRepository tenantRepository, ILandlordRepository landlordRepository)
        {
            this.db = new LetLordContext();
            this.tenantRepository = tenantRepository;
            this.landlordRepository = landlordRepository;
        }

        //
        // GET: /Search/GetAllProperties

        [HttpGet]
        public ActionResult GetAllProperties(int? page)
        {
            int pageSize = 10;
            int pageNumber = (page ?? 1);
            var model = (from x in db.ResidentialProperty select x).ToList();
            model.Sort((a, b) => b.DateAdded.Date.CompareTo(a.DateAdded.Date));
            return PartialView("_GetAllPropertiesPartial", model.ToPagedList(pageNumber, pageSize));
        }

        [HttpGet]
        public ActionResult GetAllPropertiesAdvancedSearch(GenericSearchResultsViewModel<ResidentialProperty> gsvm)
        {
            var query = (from x in db.ResidentialProperty select x);
            gsvm.SearchResults = query.ToList();
            gsvm.SearchResults.Sort((a, b) => b.DateAdded.Date.CompareTo(a.DateAdded.Date));
            return PartialView("_GetAllPropertiesAdvancedSearchPartial", gsvm);
        }

        //
        // GET: /Search/BasicPropertySearchForm

        [HttpGet]
        public ActionResult AdvancedSearch()
        {
            try
            {
                return PartialView("_AdvancedPropertySearchPartial");
            }
            catch (Exception e)
            {
                e.InnerException.ToString();
            }
            return View("Error");
        }

        // POST: /Search/BasicPropertySearch
        [HttpPost]
        public ActionResult AdvancedPropertySearch(BasicPropertySearchViewModel viewModel, GenericSearchResultsViewModel<ResidentialProperty> gsvm)
        {
            var predicate = PredicateBuilder.True<ResidentialProperty>();
            if (ModelState.IsValid)
            {
                // select all counties and apply other filters
                if (!string.IsNullOrEmpty(viewModel.County))
                {
                    if (viewModel.County == "Anywhere in Ireland")
                    {
                        predicate = predicate.And(x => x.MonthlyRate < viewModel.MaxPrice && x.MonthlyRate > viewModel.MinPrice);
                        predicate = predicate.And(x => x.NumberOfBedrooms == viewModel.NumberOfBedrooms.Substring(0,1));
                        if (!string.IsNullOrEmpty(viewModel.PropertyTypeString))
                            predicate = predicate.And(x => x.PropertyTypeString == viewModel.PropertyTypeString);
                        if (!string.IsNullOrEmpty(viewModel.NumberOfBathrooms))
                            predicate = predicate.And(x => x.NumberOfBathrooms == viewModel.NumberOfBathrooms);
                        if (!string.IsNullOrEmpty(viewModel.NumberOfReceptionRooms))
                            predicate = predicate.And(x => x.NumberOfReceptionRooms == viewModel.NumberOfReceptionRooms);
                        predicate = predicate.And(x => x.HasBackGarden == viewModel.HasBackGarden);
                        predicate = predicate.And(x => x.HasFrontGarden == viewModel.HasFrontGarden);
                        predicate = predicate.And(x => x.HasSecureParking == viewModel.HasSecureParking);
                        predicate = predicate.And(x => x.IsDisabledFriendly == viewModel.IsDisabledFriendly);

                        var results = db.ResidentialProperty.AsExpandable().Where(predicate).ToList();
                        gsvm.SearchResults = results;
                        return PartialView("_AdvancedPropertySearchResultsPartial", gsvm);
                    }
                }
                // if county selected is Dublin postcode, compare that a Property.Address.PostCode instead
                if (!string.IsNullOrEmpty(viewModel.County))
                {
                    if (viewModel.County.Substring(0, 2) == "--")
                    {
                        predicate = predicate.And(x => x.Address.PostCode.ToLower() == viewModel.County.ToLower());
                        predicate = predicate.And(x => x.MonthlyRate < viewModel.MaxPrice && x.MonthlyRate > viewModel.MinPrice);
                        predicate = predicate.And(x => x.NumberOfBedrooms == viewModel.NumberOfBedrooms.Substring(0,1));
                        if (!string.IsNullOrEmpty(viewModel.PropertyTypeString))
                            predicate = predicate.And(x => x.PropertyTypeString == viewModel.PropertyTypeString);
                        if (!string.IsNullOrEmpty(viewModel.NumberOfBathrooms))
                            predicate = predicate.And(x => x.NumberOfBathrooms == viewModel.NumberOfBathrooms);
                        if (!string.IsNullOrEmpty(viewModel.NumberOfReceptionRooms))
                            predicate = predicate.And(x => x.NumberOfReceptionRooms == viewModel.NumberOfReceptionRooms);
                        predicate = predicate.And(x => x.HasBackGarden == viewModel.HasBackGarden);
                        predicate = predicate.And(x => x.HasFrontGarden == viewModel.HasFrontGarden);
                        predicate = predicate.And(x => x.HasSecureParking == viewModel.HasSecureParking);
                        predicate = predicate.And(x => x.IsDisabledFriendly == viewModel.IsDisabledFriendly);

                        var results = db.ResidentialProperty.AsExpandable().Where(predicate).ToList();
                        gsvm.SearchResults = results;
                        return PartialView("_AdvancedPropertySearchResultsPartial", gsvm);
                    }
                }
                // if it's not Dublin, apply filters as normal
                if (!string.IsNullOrEmpty(viewModel.County))
                {
                    predicate = predicate.And(x => x.Address.County.ToLower() == viewModel.County.ToLower());
                    predicate = predicate.And(x => x.MonthlyRate < viewModel.MaxPrice && x.MonthlyRate > viewModel.MinPrice);
                    predicate = predicate.And(x => x.NumberOfBedrooms == viewModel.NumberOfBedrooms.Substring(0,1));
                    if (!string.IsNullOrEmpty(viewModel.PropertyTypeString))
                        predicate = predicate.And(x => x.PropertyTypeString == viewModel.PropertyTypeString);
                    if (!string.IsNullOrEmpty(viewModel.NumberOfBathrooms))
                        predicate = predicate.And(x => x.NumberOfBathrooms == viewModel.NumberOfBathrooms);
                    if (!string.IsNullOrEmpty(viewModel.NumberOfReceptionRooms))
                        predicate = predicate.And(x => x.NumberOfReceptionRooms == viewModel.NumberOfReceptionRooms);
                    predicate = predicate.And(x => x.HasBackGarden == viewModel.HasBackGarden);
                    predicate = predicate.And(x => x.HasFrontGarden == viewModel.HasFrontGarden);
                    predicate = predicate.And(x => x.HasSecureParking == viewModel.HasSecureParking);
                    predicate = predicate.And(x => x.IsDisabledFriendly == viewModel.IsDisabledFriendly);

                    var results = db.ResidentialProperty.AsExpandable().Where(predicate).ToList();
                    gsvm.SearchResults = results;
                    return PartialView("_AdvancedPropertySearchResultsPartial", gsvm);
                }
            }
            ModelState.AddModelError("", "Something went wrong...");
            return View("Error");
        }

        //
        // GET: /Search/SearchForTenants

        [HttpGet]
        public ActionResult SearchForTenantsForm()
        {
            try
            {
                return PartialView("_TenantSearchPartial");
            }
            catch (Exception e)
            {
                ModelState.AddModelError("", e.Message + "\n" + e.InnerException);
            }
            return View("Error");
        }

        //
        // POST: /Search/SearchForTenants

        [HttpPost]
        public ActionResult SearchForTenants(SearchForTenantViewModel viewModel, GenericSearchResultsViewModel<Tenant> gsvm)
        {
            var predicate = PredicateBuilder.True<Tenant>();
            Dictionary<int, int> weightDictionary = new Dictionary<int,int>();
            if (ModelState.IsValid)
            {
                try
                {
                    //predicate = predicate.And(x => x.HasReferences == viewModel.WantsReferenceIsSelected);

                    // if not options selected, return all tenants
                    if (viewModel.WantsStudentIsSelected == false && viewModel.WantsProfessionalIsSelected == false
                       && viewModel.AllowsRentAllowanceIsSelected == false && viewModel.AllowsPetsIsSelected == false
                       && viewModel.WantsGroupIsSelected == false && viewModel.WantsGroupIsSelected == false)
                    {
                        var selectAll = (from u in db.UserProfile.OfType<Tenant>() select u).ToList();
                        gsvm.SearchResults = selectAll;
                        return PartialView("_TenantSearchResultsPartial", gsvm);
                    }

                    if (viewModel.WantsStudentIsSelected == true)
                    {
                        predicate = predicate.And(x => x.IsStudent == viewModel.WantsStudentIsSelected);
                        weightDictionary.Add(1, viewModel.WantsStudentWeighting);
                    }

                    if (viewModel.WantsProfessionalIsSelected == true)
                    {
                        predicate = predicate.And(x => x.IsProfessional == viewModel.WantsProfessionalIsSelected);
                        weightDictionary.Add(2, viewModel.WantsProfessionalWeighting);
                    }

                    if (viewModel.AllowsRentAllowanceIsSelected == true)
                    {
                        predicate = predicate.And(x => x.HasRentAllowance == viewModel.AllowsRentAllowanceIsSelected);
                        weightDictionary.Add(3, viewModel.AllowsRentAllowanceWeighting);
                    }

                    if (viewModel.AllowsPetsIsSelected == true)
                    {
                        predicate = predicate.And(x => x.HasPets);
                        weightDictionary.Add(4, viewModel.AllowsPetsWeighting);
                    }

                    if (viewModel.WantsGroupIsSelected == true)
                    {
                        predicate = predicate.And(x => x.IsInGroup);
                        weightDictionary.Add(5, viewModel.WantsGroupWeighting);
                    }

                    if (viewModel.AllowsChildrenIsSelected == true)
                    {
                        predicate = predicate.And(x => x.HasChildren == viewModel.AllowsChildrenIsSelected);
                        weightDictionary.Add(5, viewModel.AllowsChildrenWeighting);
                    }
  
                    var results = db.UserProfile.OfType<Tenant>().AsExpandable().Where(predicate).ToList();
        
                    gsvm.SearchResults = results;

                    if (viewModel.WantsStudentIsSelected == true && weightDictionary.Values.ElementAt(0) == 3)
                        gsvm.SearchResults.OrderBy(x => x.IsStudent == true);
                    if (viewModel.WantsProfessionalIsSelected == true && weightDictionary.Values.ElementAt(1) == 3)
                        gsvm.SearchResults.OrderBy(x => x.IsProfessional == true);
                    if (viewModel.AllowsRentAllowanceIsSelected == true && weightDictionary.Values.ElementAt(2) == 3)
                        gsvm.SearchResults.OrderBy(x => x.HasRentAllowance == true);
                    if (viewModel.AllowsPetsIsSelected == true && weightDictionary.Values.ElementAt(3) == 3)
                        gsvm.SearchResults.OrderBy(x => x.HasPets == true);
                    if (viewModel.WantsGroupIsSelected == true && weightDictionary.Values.ElementAt(4) == 3)
                        gsvm.SearchResults.OrderBy(x => x.IsInGroup == true);
                    if (viewModel.AllowsChildrenIsSelected == true && weightDictionary.Values.ElementAt(5) == 3)
                        gsvm.SearchResults.OrderBy(x => x.HasChildren == true);

                    return PartialView("_TenantSearchResultsPartial", gsvm);
                }
                catch(Exception e)
                {
                    ModelState.AddModelError("", e.Message + "\n" + e.InnerException);
                }
            }
            return View("Error");
        }

        //
        // GET: /Search/AdvancedPropertySearchForm

        [HttpGet]
        public ActionResult QuickPropertySearchForm()
        {
            try
            {
                return PartialView("_QuickPropertySearchFormPartial");
            }
            catch (Exception e)
            {
                e.InnerException.ToString();
            }
            return View("Error");
        }

        //
        // POST: /Search/AdvancedPropertySearch

        [HttpPost]
        public ActionResult QuickPropertySearch(QuickPropertySearchViewModel viewModel, GenericSearchResultsViewModel<ResidentialProperty> gsvm)
        {
            var predicate = PredicateBuilder.True<ResidentialProperty>();
            if (ModelState.IsValid)
            {
                try
                {
                    // select all counties and apply other filters
                    if(!string.IsNullOrEmpty(viewModel.County))
                    {
                        if (viewModel.County == "Anywhere in Ireland") 
                        {
                            predicate = predicate.And(x => x.MonthlyRate < viewModel.MaxPrice && x.MonthlyRate > viewModel.MinPrice);
                            predicate = predicate.And(x => x.NumberOfBedrooms == viewModel.DesiredNumBeds.Substring(0,1));
                            var results = db.ResidentialProperty.AsExpandable().Where(predicate).ToList();
                            gsvm.SearchResults = results;
                            return PartialView("_QuickPropertySearchResultsPartial", gsvm);
                        }
                    }
                    // if county selected is Dublin postcode, compare that a Property.Address.PostCode instead
                    if (!string.IsNullOrEmpty(viewModel.County))
                    {
                        if (viewModel.County.Substring(0,2) == "--")
                        {
                            predicate = predicate.And(x => x.Address.PostCode.ToLower() == viewModel.County.ToLower());
                            predicate = predicate.And(x => x.MonthlyRate < viewModel.MaxPrice && x.MonthlyRate > viewModel.MinPrice);
                            predicate = predicate.And(x => x.NumberOfBedrooms == viewModel.DesiredNumBeds.Substring(0,1));
                            var results = db.ResidentialProperty.AsExpandable().Where(predicate).ToList();
                            gsvm.SearchResults = results;
                            return PartialView("_QuickPropertySearchResultsPartial", gsvm);
                        }
                    }
                    // if it's not Dublin, apply filters as normal
                    if (!string.IsNullOrEmpty(viewModel.County))
                    {
                        predicate = predicate.And(x => x.Address.County.ToLower() == viewModel.County.ToLower());
                        predicate = predicate.And(x => x.MonthlyRate < viewModel.MaxPrice && x.MonthlyRate > viewModel.MinPrice);
                        predicate = predicate.And(x => x.NumberOfBedrooms == viewModel.DesiredNumBeds.Substring(0,1));
                        var results = db.ResidentialProperty.AsExpandable().Where(predicate).ToList();
                        gsvm.SearchResults = results;
                        return PartialView("_QuickPropertySearchResultsPartial", gsvm);
                    }                  
                }
                catch (Exception e)
                {
                    ModelState.AddModelError("", e.Message + "\n" + e.InnerException);
                }
            }
            return View("Error");
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}
